export class LongBits {

    lo: number
    hi: number

    constructor(lo: number, hi: number) {
        this.lo = lo;
        this.hi = hi;
    }

    static from(num: number, out?: LongBits) {
        if (num === 0) return zero;
        let sign = num < 0;
        if (sign) num = -num;
        let lo = num >>> 0,
            hi = (num - lo) / 4294967296 >>> 0;
        if (sign) {
            hi = ~hi >>> 0;
            lo = ~lo >>> 0;
            if (++lo > 4294967295) {
                lo = 0;
                if (++hi > 4294967295) hi = 0;
            }
        }
        return out ? out.set(lo, hi) : new LongBits(lo, hi);
    }

    static fromUnsigned(num: number, out?: LongBits) {
        if (num === 0) return zero;
        let lo = num >>> 0,
            hi = (num - lo) / 4294967296 >>> 0;
        return out ? out.set(lo, hi) : new LongBits(lo, hi);
    }

    set(lo: number, hi: number) {
        this.lo = lo;
        this.hi = hi;
        return this;
    }

    get num() {
        if (this.hi >>> 31) {
            let lo = ~this.lo + 1 >>> 0,
                hi = ~this.hi >>> 0;
            if (!lo) hi = hi + 1 >>> 0;
            return -(lo + hi * 4294967296);
        }
        return this.lo + this.hi * 4294967296;
    }

    get unum() {
        return this.lo + this.hi * 4294967296;
    }

    get zzEncode() {
        let mask = this.hi >> 31;
        this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
        this.lo = (this.lo << 1 ^ mask) >>> 0;
        return this;
    }

    get zzDecode() {
        let mask = -(this.lo & 1);
        this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
        this.hi = (this.hi >>> 1 ^ mask) >>> 0;
        return this;
    }

    get length() {
        let part0 = this.lo,
            part1 = (this.lo >>> 28 | this.hi << 4) >>> 0,
            part2 = this.hi >>> 24;
        return part2 === 0
            ? part1 === 0
                ? part0 < 16384
                    ? part0 < 128 ? 1 : 2
                    : part0 < 2097152 ? 3 : 4
                : part1 < 16384
                    ? part1 < 128 ? 5 : 6
                    : part1 < 2097152 ? 7 : 8
            : part2 < 128 ? 9 : 10;
    }

}

const zero = new LongBits(0, 0);
Object.defineProperties(zero, {
    "lo": { value: 0, writable: false },
    "hi": { value: 0, writable: false },
    "num": { value: 0, writable: false },
    "unum": { value: 0, writable: false },
    "zzEncode": { value: zero, writable: false },
    "zzDecode": { value: zero, writable: false },
    "length": { value: 1, writable: false }
});